﻿
// ## 2023/03/11 # 铃 # 完成,AI可以把把武将送到没有武将但是有资源的小城,并把资源送到大城.
// ## 2022/11/11 # 铃 # 初稿,初衷是当小城市有钱有粮草,但是没有武将的时候,调度过来运输

namespace AI小城市调度
{
	const int 召唤城市距离 = 1;

	/// =========================================================================================

	const bool 调试模式 = false;
	const bool 调试模式2 = false;
	const bool 新算法 = true;

	const int KEY = pk::hash("AI小城市调度");

	class Main
	{

		Main()
		{
			pk::bind(202, pk::trigger202_t(onAIRunningOrder));
		}

		void onAIRunningOrder(pk::ai_context @context, pk::building @building, int cmd)
		{
			if (cmd == 据点AI_小城召唤)
			{
				pk::force @force = pk::get_force(building.get_force_id());
				pk::ai_context_base @base = context.base[building.get_id()];
				int building_id = building.get_id();
				int src_id = -1;

				// 如果是大城市则不执行
				if (building.get_id() < 城市_末)
					return;

				// 如果是玩家城市,就退出
				if (building.is_player())
					return;

				// 这里需要计算一下大概全势力有多少人,多少城市,小城大约需要多少人
				// 这里要用军团,不允许垮军团调度,是因为玩家的军团逻辑在255中所限制.
				pk::district @district = pk::get_district(building.get_district_id());

				// 大城市2倍权重.计算两次
				// 考虑到别的城市可能处于战争状态或者前线之类的,基准数字下调一些.(1人左右),在小城自己判断的时候再加上
				// 这个人数制约了小城非战斗状态下的,正常调度获得的人数上限.
				// 假如势力有10个人,1大城1小城,这时候基准需求则为2人.大城8人
				int 基准需求 = pk::min(ch::base_num_person_count_force(building), 5);

				// 战斗据点,人数需求+3,即使其他据点人数不多,小城依然需要索取,大不了在来源城市计算再拒绝掉.
				// 假如势力有10个人,1大城1小城,这时候基准需求则为5人.大城5人
				if (ai::base_in_range_enemy_troops[building_id] > 0)
					基准需求 += 3;

				// 危险据点
				else if (ch::neibor_enemy_base_count(pk::get_building(building_id), 1) >= 3)
					基准需求 += 2;

				// 前线据点
				else if (ch::neibor_enemy_base_count(pk::get_building(building_id), 1) >= 1)
					基准需求 += 1;

				// 不需要超过8个人
				基准需求 = pk::min(基准需求, 8);

				// 关隘内政能力太差了,不需要留下那么多人
				if (ch::is_gate(building.get_id()) and ai::base_in_range_enemy_troops[building_id] == 0)
					基准需求 = pk::min(基准需求, 3);

				// 如果满足正常需求,则不用再调度
				if (pk::get_person_list(building, pk::mibun_flags(身份_君主, 身份_都督, 身份_太守, 身份_一般)).count >= 基准需求 - 1)
					return;

				int 调度需求人数 = 基准需求 - pk::get_person_list(building, pk::mibun_flags(身份_君主, 身份_都督, 身份_太守, 身份_一般)).count;

				// 最少需要1人
				调度需求人数 = pk::max(调度需求人数, 1);

				if (building.get_id() == 铃据点_梁国 and 调试模式2)
					pk::trace(pk::format("调度需求人数  ：{}", 调度需求人数));

				// 获取距离1以内的,人数最多城市.
				// 距离为1 主要是为了防止在路上的人没有被计入小城,重复操作导致.
				// 未来再考虑距离为2,路上的问题
				src_id = get_best_base(building, 调度需求人数);

				// 如果获取不到则退出.
				if (src_id == -1)
					return;

				if (building.get_id() == 铃据点_梁国 and 调试模式2)
					pk::trace(pk::format("src_id  ：{}", src_id));

				// 获取源城市武将列表,并且按照能力最低来排序

				auto src_person_idle_list = pk::get_idle_person_list(pk::get_building(src_id));
				auto person_list = pk::get_person_list(pk::get_building(src_id), pk::mibun_flags(身份_都督, 身份_太守, 身份_一般));

				// 召唤人数是能获得的人数和所需人数的最小值.
				int 召唤人数 = pk::min(调度需求人数, person_list.count - ch::normal_calc_reserve_person_count(src_id), src_person_idle_list.count);

				if (building.get_id() == 铃据点_梁国 and 调试模式2)
					pk::trace(pk::format("召唤人数  ：{},总人数  ：{},自己需要  ：{}", 召唤人数, person_list.count, ch::normal_calc_reserve_person_count(src_id)));

				bool 兵临 = pk::enemies_around(building);
				bool 战斗 = ai::base_in_range_enemy_troops[building_id] > 0;
				bool 危险 = ch::neibor_enemy_base_count(pk::get_building(building_id), 1) >= 3;
				bool 前线 = ch::neibor_enemy_base_count(pk::get_building(building_id), 1) >= 1;

				// 排序的时候也要分情况
				// 小城市如果是兵临城下状态,优先调度战斗力强的人.
				// 小城市并不适合把最好的人调走,因此按照增加功绩排序,越小越优先
				if (兵临)
				{
					person_list.sort(function(a, b) {
						return (int(a.kouseki / 30) - a.stat[武将能力_统率] - a.stat[武将能力_武力]) + pk::rand(50) < (int(b.kouseki / 30) - b.stat[武将能力_统率] - b.stat[武将能力_武力] + pk::rand(50));
					});
				}

				// 小城市如果是战斗状态,考虑混合调动
				// 小城市并不适合把最好的人调走,因此按照增加功绩排序,越小越优先
				else if (战斗)
				{
					person_list.sort(function(a, b) {
						return (int(a.kouseki / 30) - a.stat[武将能力_政治] - a.stat[武将能力_武力]) + pk::rand(50) < (int(b.kouseki / 30) - b.stat[武将能力_政治] - b.stat[武将能力_武力] + pk::rand(50));
					});
				}

				// 小城市如果是危险前线状态,那么就要考虑多智力.
				// 小城市并不适合把最好的人调走,因此按照增加功绩排序,越小越优先
				else if (危险)
				{
					if (building.get_id() == 铃据点_梁国 and 调试模式2)
						pk::trace(pk::format("威胁:召唤人数  ：{},总人数  ：{},自己需要  ：{}", 召唤人数, person_list.count, ch::normal_calc_reserve_person_count(src_id)));
					person_list.sort(function(a, b) {
						return (int(a.kouseki / 30) - a.stat[武将能力_政治] - a.rank - a.stat[武将能力_智力] + a.stat[武将能力_统率] * +pk::rand(30)) < (int(b.kouseki / 30) - b.stat[武将能力_政治] - b.rank - b.stat[武将能力_智力] + a.stat[武将能力_统率] + pk::rand(30));
					});
				}

				// 小城市如果是普通前线状态,那么就要考虑多政治.
				// 小城市并不适合把最好的人调走,因此按照增加功绩排序,越小越优先
				// 同时也要刻意剔除高统率的
				else if (前线)
				{
					if (building.get_id() == 铃据点_梁国 and 调试模式2)
						pk::trace(pk::format("前线:召唤人数  ：{},总人数  ：{},自己需要  ：{}", 召唤人数, person_list.count, ch::normal_calc_reserve_person_count(src_id)));
					person_list.sort(function(a, b) {
						return (int(a.kouseki / 30) + a.stat[武将能力_武力] - a.rank + pk::rand(30)) < (int(b.kouseki / 30) + b.stat[武将能力_武力] - b.rank + pk::rand(30));
					});
				}

				// 小城市如果是后方状态,那么就要考虑多政治.,要刻意剔除高武力和高统帅
				// 小城市并不适合把最好的人调走,因此按照增加功绩排序,越小越优先
				else
				{
					if (building.get_id() == 铃据点_梁国 and 调试模式2)
						pk::trace(pk::format("后方:召唤人数  ：{},总人数  ：{},自己需要  ：{}", 召唤人数, person_list.count, ch::normal_calc_reserve_person_count(src_id)));
					person_list.sort(function(a, b) {
						return (int(a.kouseki / 30) + a.stat[武将能力_统率] * 2 + a.stat[武将能力_武力] + a.stat[武将能力_政治] + pk::rand(30)) < (int(b.kouseki / 30) + b.stat[武将能力_统率] * 2 + b.stat[武将能力_武力] + b.stat[武将能力_政治] + pk::rand(30));
					});
				}

				if (新算法)
				{
					person_list.sort(function(a, b) {
						return (a.stat[武将能力_统率] + a.stat[武将能力_武力] + a.stat[武将能力_智力]) > (b.stat[武将能力_统率] + b.stat[武将能力_武力] + b.stat[武将能力_智力]);
					});
				}

				if (building.get_id() == 铃据点_梁国 and 调试模式2)
					pk::trace(pk::format("前线:召唤人数  ：{},总人数  ：{},可用人数  ：{}", 召唤人数, person_list.count, src_person_idle_list.count));

				// 召唤对象武将

				pk::list<pk::person @> actors;
				int j = 0;
				for (int i = 0; i < person_list.count; i++)
				{
					// 兵临状态下,属性最好的1/12跳过
					if (兵临)
					{
						if (i < person_list.count / 12)
							continue;
					}
					// 战斗状态下,属性最好的1/8位跳过
					else if (战斗)
					{
						if (i < person_list.count / 8)
							continue;
					}
					// 战斗状态下,属性最好的1/5位跳过
					else if (危险)
					{
						if (i < person_list.count / 5)
							continue;
					}
					// 前线状态下,属性最好的1/3位跳过
					else if (前线)
					{
						if (i < person_list.count / 3)
							continue;
					}
					// 后方状态下,属性最好的2/3位跳过
					else
					{
						if (i < person_list.count / 3 * 2)
							continue;
					}

					if (pk::is_absent(person_list[i]) or pk::is_unitize(person_list[i]))
						continue;
					if (person_list[i].action_done == true)
						continue;
					if (person_list[i].order == 武将任务_移动)
						continue;
					// 一些特别重要的大城市才有用的特技,就不要调度走了
					if (ch::has_skill(person_list[i], 特技_富豪) or ch::has_skill(person_list[i], 特技_征税) or ch::has_skill(person_list[i], 特技_米道) or ch::has_skill(person_list[i], 特技_征收))
						continue;
					j++;
					actors.add(person_list[i]);
					person_list[i].action_done = true;
					string person_name2 = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(person_list[i])));

					if (调试模式)
					{
						string person_name = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(person_list[i])));
						string building_name = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(building)));
						string dst_name = pk::format("\x1b[1x{}\x1b[0x", pk::decode(pk::get_name(pk::get_building(src_id))));
						string action_message = pk::format("{}从{}召唤了{}", building_name, dst_name, person_name);
						pk::trace(pk::format("{}从{}召唤了{}", building_name, dst_name, person_name));
					}
					if (j >= 召唤人数)
						break;
				}

				// 召唤命令
				pk::summon_cmd_info cmd_summon;
				@cmd_summon.base = building;

				cmd_summon.actors = actors;
				pk::command(cmd_summon);

				ch::remove_idle_person(context, actors);
			}
			return;
		}

		int get_best_base(pk::building @dst, int 调度需求人数)
		{
			pk::list<pk::building @> list;
			int j = dst.get_id();

			for (int i = 0; i < 87; i++)
			{
				pk::building @src = pk::get_building(i);

				if (src.get_force_id() == -1 or src.get_force_id() >= 势力_末)
					continue;

				// 如果是玩家城市,就退出
				if (src.is_player() or dst.is_player())
					continue;

				// 如果选到自己就跳过
				if (dst.get_id() == i)
					continue;

				// 如果距离太远就跳过
				if (pk::get_building_distance(dst.get_id(), i, dst.get_force_id()) > 召唤城市距离)
					continue;

				// 如果不同势力就跳过
				if (dst.get_force_id() != src.get_force_id())
					continue;

				if (j == 铃据点_梁国 and 调试模式2)
					pk::trace(pk::format("{}到{}距离为{}", pk::get_new_base_name(i), pk::get_new_base_name(j), pk::get_building_distance(j, i, pk::get_building(i).get_force_id())));

				// 如果除了君主之外的人数小于3个就跳过
				auto src_person_list = pk::get_person_list(src, pk::mibun_flags(身份_都督, 身份_太守, 身份_一般));
				auto src_idle_person_list = pk::get_idle_person_list(src);

				// 敌军在境内,且武将不多的时候不调度.
				if (ai::base_in_range_enemy_troops[i] > 0 and src_person_list.count < 15)
					continue;

				if (j == 铃据点_梁国 and 调试模式2)
					pk::trace(pk::format("总人数:{},自己需要:{},可用人数{}", src_person_list.count, ch::normal_calc_reserve_person_count(i), src_idle_person_list.count));

				// 召唤城市不仅要人数够用,而且需要1个以上的冗余,
				// 召唤城市自己不能处于战斗状态
				if (src_person_list.count >= ch::normal_calc_reserve_person_count(i) + 1 and ch::enemy_troop_base_near_distance(src, 5) == 0 and src_idle_person_list.count > 0)
					list.add(src);
			}
			if (list.count > 0)
			{
				// 排序的标准是:人越多分越高,距离近的分高,小城市分高
				list.sort(function(a, b) {
					int stat_a = pk::get_idle_person_list(a).count;
					int stat_b = pk::get_idle_person_list(b).count;
					return (stat_a > stat_b);
				});

				return (list[0].get_id());
			}
			else
				return -1;
		}

	} Main main;
}